In this notebook we are comparing the use of Alevin-fry and Spaceranger for quantifying spatial transcriptomics libraries. Two spatial transcriptomic libraries were quantified using Alevin-fry and Spaceranger and the results were combined following the Alevin-fry tutorial. We are comparing that to if we were to not integrate the Spaceranger data with Alevin-fry and only use Spaceranger. For simplicity we will only look at one library, SCPCR000372.

Set Up

library(magrittr)
library(ggplot2)
library(SingleCellExperiment)
library(SpatialExperiment)
library(ggupset)
library(gridExtra)
library(ggrepel)
library(clusterProfiler)
library(org.Hs.eg.db)
# load in benchmarking functions that will be used for copying data and generating sample tables
function_path <- file.path(".." ,"benchmarking-functions", "R")
file.path(function_path, list.files(function_path, pattern = "*.R$")) %>%
  purrr::walk(source)
# set up file paths 
base_dir <- here::here()

# folder with alevin-fry and cellranger quants from S3
data_dir <- file.path(base_dir, "data", "spatial") 
quants_dir <- file.path(data_dir, "data", "quants")

# results directory 
results_dir <- file.path(data_dir, "results")

# sample name
sample_id <- c("SCPCR000372")
mito_file <- file.path(base_dir, "sample-info", "Homo_sapiens.GRCh38.103.mitogenes.txt")
  
# read in mito genes 
mito_genes <- readr::read_tsv(mito_file, col_names = "gene_id")
Rows: 111 Columns: 1
── Column specification ───────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): gene_id

β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
mito_genes <- mito_genes %>%
  dplyr::pull(gene_id) %>%
  unique()

Alevin-fry + Spaceranger versus Spaceranger Only

Now let’s take a look at comparing the two methods of using Alevin-fry + Spaceranger to only Spaceranger for quantification. To do this, we will read in the Alevin-fry + Spaceranger combined and Spaceranger only SpatialExperiment objects separately and then merge them into one list before grabbing the per cell and per gene quality metrics.

Create Spatial Experiments

# get path to fry output directory 
fry_dir <- file.path(quants_dir, "alevin-fry-knee", sample_id)
fry_dir <- paste0(fry_dir, "-spliced_intron_txome_k31-salign-cr-like-em-knee")

# paths to spatial folders 
cellranger_folder <- paste0(sample_id, "-cdna-spatial")
spaceranger_dir <- file.path(quants_dir, "cellranger", cellranger_folder)
# read in combined fry and spaceranger spe 
fry_spe <- create_fry_spaceranger_spe(fry_dir, 
                                      spaceranger_dir, 
                                      sample_id)
Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.
Joining, by = "barcode"
# spaceranger output paths
spaceranger_dir <- file.path(quants_dir, "cellranger", cellranger_folder)

# read in spaceranger output directly using read10XVisium
spaceranger_spe <- create_spaceranger_spe(spaceranger_dir, sample_id)
Rows: 4992 Columns: 6
── Column specification ───────────────────────────────────────────────────────────
Delimiter: ","
chr (1): barcode
dbl (5): in_tissue, array_row, array_col, pxl_row_in_fullres, pxl_col_in_fullres

β„Ή Use `spec()` to retrieve the full column specification for this data.
β„Ή Specify the column types or set `show_col_types = FALSE` to quiet this message.

Per Spot QC Metrics

Now that we have read in the data and created our two SpatialExperiment objects, we can go ahead and combine them into one list and then calculate the per spot QC metrics using scuttle::addPerCellQCMetrics().

# create one list with both spe's together
all_spe_list <- list(fry_spe, spaceranger_spe)
names(all_spe_list) <- c("alevin-fry", "spaceranger")

# calculate per cell QC and output to a combined data frame with plotting 
all_spe_list <- all_spe_list %>%
    purrr::map(
      ~ scuttle::addPerCellQCMetrics(.x, 
                                     subsets = list(mito = mito_genes[mito_genes %in% rownames(.x)])))

After adding in the per spot QC metrics to both of the spe’s, we want to extract the colData from each spe and create a data frame that we can use for plotting. We will also need some information about each sample and how it was run, so we will create a sample metadata table, sample_info_df that will then be merged with the colData.

# create sample info dataframe to be joined with per spot dataframe later
sample_info_df <- quant_info_table(data_dir= quants_dir, 
                 tools = c("cellranger", "alevin-fry-knee"),
                 samples = sample_id) %>%
  # convert cellranger to spaceranger 
  dplyr::mutate(tool = ifelse(tool == "cellranger", "spaceranger", tool))

sample_info_df

When we convert the colData to a data frame we use the custom function, spatial_coldata_to_df() to do so and apply it to each spe in our list.

# join coldata dataframe with sample info
coldata_df <- all_spe_list %>%
  purrr::map_df(spatial_coldata_to_df, .id = "tool") %>%
  # remove extra -1 from spaceranger barcodes
  dplyr::mutate(spot_id = gsub("-1", "", spot_id)) %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool", "sample_id" = "sample"))

Now we only want to filter our data frame to contain spots that are shared between both tools and those that are found to be overlapping with the tissue.

# identify shared spots only 
spot_counts <- coldata_df %>%  
  dplyr::count(spot_id, sample_id)
# how many spots are shared among the tools
spot_counts_plot <- coldata_df %>%
  dplyr::group_by(spot_id) %>%
  dplyr::summarise(tools_detected = list(unique(tool)))

ggplot(spot_counts_plot, aes(x = tools_detected))+
  geom_bar() +
  scale_x_upset(n_intersections = 3)

For the most part, the majority of the spots identified are found in both Spaceranger alone and the combination with Alevin-fry, with a small subset being identified in Spaceranger alone.

Let’s filter to only include these common spots and those that are found to be overlapping the tissue.

common_spots <- spot_counts %>%
  dplyr::filter(n == 2) %>%
  dplyr::pull(spot_id)

coldata_df_common <- coldata_df %>%
  dplyr::filter(spot_id %in% common_spots,
                # only include spots that overlap with tissue
                in_tissue == 1)

We will also need to filter the spe’s directly based on spots that are present in the tissue, so we create a small function to do this and then apply it to both spe’s in the list.

# we will also want to filter the spe's directly 
filter_spe <- function(spe){
  spe <- spe[, spatialData(spe)$in_tissue == 1]
}

all_spe_filter <- all_spe_list %>%
  purrr::map(filter_spe)

When we look at our results, we will also want to visualize them so we will make a custom function to plot the results.

# custom function for plotting spe results and coloring by column of colData of choice
plot_spe <- function(spe, sample, column){
  # plot spots only 
  p1 <- ggspavis::plotSpots(spe, 
                            x_coord = "pxl_col_in_fullres", 
                            y_coord = "pxl_row_in_fullres", 
                            annotate = column) +
    scale_color_viridis_c()
  
  # plot with tissue underneath
  p2 <- ggspavis::plotVisium(spe, 
                                   x_coord = "pxl_col_in_fullres", 
                                   y_coord = "pxl_row_in_fullres", 
                                   fill = column) +
    scale_fill_viridis_c()
  
  # arrange plots and add sample name as title 
  grid.arrange(p1, p2, nrow = 1, top = grid::textGrob(sample))
}

First we will look at the per cell metrics: mitochondrial reads per cell, total UMI per cell, and total genes detected per cell.

# % mitochondrial reads/ spot 
ggplot(coldata_df_common, aes(x = tool, y = subsets_mito_percent, fill = tool)) + 
  geom_boxplot() +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Mito Percent") + 
  xlab("")

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "subsets_mito_percent")
Scale for 'colour' is already present. Adding another scale for 'colour', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will
replace the existing scale.

Overall it looks like mitochondrial content is low and fairly similar across both tools.

# total UMI/ spot 
ggplot(coldata_df_common, aes(x = sum, color = tool)) + 
  geom_density() + 
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("UMI/spot") + 
  xlab("")

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "sum")
Scale for 'colour' is already present. Adding another scale for 'colour', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will
replace the existing scale.

# total genes/ spot 
ggplot(coldata_df_common, aes(x = detected, color = tool)) + 
  geom_density() + 
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) + 
  ylab("Genes detected/spot") + 
  xlab("") 

all_spe_filter %>%
  purrr::iwalk(plot_spe, column = "detected")
Scale for 'colour' is already present. Adding another scale for 'colour', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will
replace the existing scale.

Generally it looks like there are some differences between the tools. When looking at this closer, it appears that Alevin-fry provides an increase in both total UMI/cell and genes detected/cell which is seen in the spatial plot as well.

Per Gene QC Metrics

Let’s also look at the correlation of mean gene expression across shared genes. We will first need to calculate the per feature QC on the filtered spes after removing spots not present in the tissue and then grab the rowData and combine into a data frame used for plotting.

all_spe_filter <- all_spe_filter %>%
  purrr::map(scuttle::addPerFeatureQCMetrics)
# grab rowdata and combine with sample info
rowdata_df <- purrr::map_df(all_spe_filter, scpcaTools::rowdata_to_df, .id = "tool") %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool"))

We then want to filter out any lowly detected genes, (detected < 5.0) and restrict our analysis to those genes that are found in both tools.

gene_counts <- rowdata_df %>% 
  # remove genes that have a low frequency of being detected
  dplyr::filter(detected >= 5.0) %>%
  dplyr::count(gene_id, sample)

# restrict to only common genes 
common_genes <- gene_counts %>%
  dplyr::filter(n == 2) %>%
  dplyr::pull(gene_id)

rowdata_df_common <- rowdata_df %>%
  dplyr::filter(gene_id %in% common_genes) 
# create a table to calculate correlation between mean gene expression
rowdata_cor <- rowdata_df_common %>%
  dplyr::select(tool, gene_id, sample, mean) %>%
  # spread the mean expression stats to one column per caller
  tidyr::pivot_wider(id_cols = c(gene_id, sample),
                     names_from = c("tool"),
                     values_from = mean) %>%
  # drop rows with NA values to ease correlation calculation
  tidyr::drop_na()
# look at correlation between the two tools
rowdata_cor %>% 
  dplyr::group_by(sample) %>%
  dplyr::summarize(
    alevin_fry_knee_spaceranger_cor = cor(`spaceranger`, `alevin-fry`, method = "spearman")
  )
# mean gene expression across shared genes 
ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  geom_abline() +
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Mean gene expression") + 
  theme_classic()

NA

Correlation appears to be quite high between mean gene expression in Spaceranger and Alevin-fry, however, we do see that there is a subset of genes that have higher gene expression in Spaceranger than in Alevin-fry and are slightly off the diagonal.

# get the gene symbols and then join back with rowdata df 
gene_symbols_df <- rowdata_df %>%
  dplyr::select(gene_id, symbol)

# join correlation with gene symbols 
rowdata_cor <- rowdata_cor %>%
  dplyr::left_join(gene_symbols_df) %>%
  dplyr::distinct() %>%
  tidyr::drop_na() %>%
  # add difference in mean gene expression between alevin-fry and spaceranger 
  dplyr::mutate(log_fold_change = log(`alevin-fry`/spaceranger))
Joining, by = "gene_id"
# print out list of genes with with > 1.5 gene expression in Alevin-fry
rowdata_cor %>%
  dplyr::filter(abs(log_fold_change) > 0.5) %>%
  dplyr::arrange(desc(log_fold_change)) %>%
  dplyr::select(sample, symbol, gene_id, log_fold_change, `alevin-fry`, spaceranger)
ggplot(rowdata_cor, aes(x = `spaceranger`, y = `alevin-fry`)) +
  geom_point(size = 0.5, alpha = 0.1) + 
  scale_x_log10() + 
  scale_y_log10() + 
  geom_abline() +
  labs(x = "Spaceranger mean gene expression", y = "Alevin Fry Mean gene expression") + 
  theme_classic() + 
  geom_text_repel(data = rowdata_cor %>%
                    dplyr::filter(abs(log_fold_change) > 0.5),
                  mapping = aes(x = `spaceranger`, y = `alevin-fry`, label = symbol),
                  max.overlaps= 50, size= 2)

Let’s look at what types of genes are found to have different gene expression across these two tools using over representation analysis.

# target gene list 
different_genes <- rowdata_cor %>%
  # filter for anything with fold change > 1.5
  dplyr::filter(abs(log_fold_change) > 0.4) %>%
  dplyr::pull(symbol) %>%
  unique()

# background gene list 
background_genes <- rowdata_cor$symbol %>%
  unique()
# perform gene ontology looking at all genes that are different 
go_ora_results <- enrichGO(gene = different_genes,
                           universe = background_genes,
                           keyType = "SYMBOL",
                           OrgDb = org.Hs.eg.db,
                           ont = "BP",
                           pAdjustMethod = "BH",
                           pvalueCutoff = 0.00001)
# look at gene ontology results 
go_results <- go_ora_results@result %>%
  as.data.frame() %>%
  dplyr::filter(p.adjust < 0.00001)
go_results

It looks like there aren’t any specific pathways that are affected.

Shared genes across tools

The final thing we will look at is the overlap of genes detected in each tool. First we need to filter the SpatialExperiment objects to only have spots that are found in both tools and then we filter to include only genes that are found in both indices before looking at the overlap.

# make a function to filter sces by subset of spots and re-calculate feature stats
filter_spe <- function(spe, spots){
  # remove "-1" at end of barcode for spaceranger spes
  colnames(spe) <- gsub("-1", "", colnames(spe))
  cells_to_keep <- colnames(spe) %in% spots
  rowData(spe) <- NULL
  spe[, cells_to_keep] %>%
    scuttle::addPerFeatureQCMetrics()
}
# filter all spes to only have common spots 
spe_list_common <- all_spe_filter %>%
  purrr::map(filter_spe, spots = common_spots)

# grab rowdata from filtered sces 
rowdata_df_filtered <- purrr::map_df(spe_list_common, scpcaTools::rowdata_to_df, .id = "tool") %>%
  dplyr::left_join(sample_info_df,
                   by = c("tool"))
# get genes common in all tools
common_genes <- rowdata_df_filtered %>%
  dplyr::select(gene_id, tool) %>%
  dplyr::distinct() %>%
  dplyr::group_by(gene_id) %>%
  dplyr::tally() %>%
  # filter for genes found in both spaceranger and alevin-fry
  dplyr::filter(n == 2) %>%
  dplyr::pull(gene_id)

# filter rowdata_df to only include genes found in all tools and genes with mean > 0 and detected > 0 in all cells
rowdata_df_filtered <- rowdata_df_filtered %>% 
  dplyr::filter(gene_id %in% common_genes) %>%
  dplyr::filter(mean > 0 & detected > 0)
# create upset plot 
gene_detect_df <- rowdata_df_filtered %>%
  dplyr::group_by(gene_id) %>%
  dplyr::summarise(tools_detected = list(unique(tool)))

ggplot(gene_detect_df, aes(x = tools_detected)) +
  geom_bar() +
  scale_x_upset(n_intersections = 3)

Similar to with the single-cell data most genes are detected in both Alevin-fry and Spaceranger which is good.

Concluding thoughts

  • Alevin-fry and Spaceranger only result in similar distributions of UMI/spot and genes/spot, although it is not quite as nice as the overlay you see with single-cell libraries.
  • It appears that Alevin-fry has slightly higher UMI/cell and genes detected/cell.
  • Both tools also show high correlation in mean gene expression and high overlap in the genes that are detected, although there is a subset of genes that are off the diagonal but have no particular gene set enrichment.

Session Info

sessioninfo::session_info()
─ Session info  🎸  πŸ‘ž  🀲   ────────────────────────────────────────────────────
 hash: guitar, man’s shoe, palms up together

 setting  value
 version  R version 4.1.1 (2021-08-10)
 os       macOS Catalina 10.15.7
 system   x86_64, darwin17.0
 ui       RStudio
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/Chicago
 date     2021-11-30
 rstudio  1.4.1106 Tiger Daylily (desktop)
 pandoc   2.11.4 @ /Applications/RStudio.app/Contents/MacOS/pandoc/ (via rmarkdown)

─ Packages ──────────────────────────────────────────────────────────────────────
 ! package              * version  date (UTC) lib source
   AnnotationDbi        * 1.56.2   2021-11-09 [1] Bioconductor
   ape                    5.5      2021-04-25 [1] CRAN (R 4.1.0)
   aplot                  0.1.1    2021-09-22 [1] CRAN (R 4.1.0)
   assertthat             0.2.1    2019-03-21 [1] CRAN (R 4.1.0)
   beachmat               2.10.0   2021-10-26 [1] Bioconductor
   beeswarm               0.4.0    2021-06-01 [1] CRAN (R 4.1.0)
   Biobase              * 2.54.0   2021-10-26 [1] Bioconductor
   BiocGenerics         * 0.40.0   2021-10-26 [1] Bioconductor
   BiocManager            1.30.16  2021-06-15 [1] CRAN (R 4.1.0)
   BiocNeighbors          1.12.0   2021-10-26 [1] Bioconductor
   BiocParallel           1.28.2   2021-11-25 [1] Bioconductor
   BiocSingular           1.10.0   2021-10-26 [1] Bioconductor
   Biostrings             2.62.0   2021-10-26 [1] Bioconductor
   bit                    4.0.4    2020-08-04 [1] CRAN (R 4.1.0)
   bit64                  4.0.5    2020-08-30 [1] CRAN (R 4.1.0)
   bitops                 1.0-7    2021-04-24 [1] CRAN (R 4.1.0)
   blob                   1.2.2    2021-07-23 [1] CRAN (R 4.1.0)
   bslib                  0.3.1    2021-10-06 [1] CRAN (R 4.1.0)
   cachem                 1.0.6    2021-08-19 [1] CRAN (R 4.1.0)
   cli                    3.1.0    2021-10-27 [1] CRAN (R 4.1.1)
   clusterProfiler      * 4.2.0    2021-10-26 [1] Bioconductor
   colorspace             2.0-2    2021-06-24 [1] CRAN (R 4.1.0)
   crayon                 1.4.2    2021-10-29 [1] CRAN (R 4.1.0)
   data.table             1.14.2   2021-09-27 [1] CRAN (R 4.1.0)
   DBI                    1.1.1    2021-01-15 [1] CRAN (R 4.1.0)
   DelayedArray           0.20.0   2021-10-26 [1] Bioconductor
   DelayedMatrixStats     1.16.0   2021-10-26 [1] Bioconductor
   digest                 0.6.28   2021-09-23 [1] CRAN (R 4.1.0)
   DO.db                  2.9      2021-11-30 [1] Bioconductor
   DOSE                   3.20.1   2021-11-18 [1] Bioconductor
   downloader             0.4      2015-07-09 [1] CRAN (R 4.1.0)
   dplyr                  1.0.7    2021-06-18 [1] CRAN (R 4.1.0)
   dqrng                  0.3.0    2021-05-01 [1] CRAN (R 4.1.0)
   DropletUtils           1.14.1   2021-11-08 [1] Bioconductor
   edgeR                  3.36.0   2021-10-26 [1] Bioconductor
   ellipsis               0.3.2    2021-04-29 [1] CRAN (R 4.1.0)
   enrichplot             1.14.1   2021-10-31 [1] Bioconductor
   evaluate               0.14     2019-05-28 [1] CRAN (R 4.1.0)
   fansi                  0.5.0    2021-05-25 [1] CRAN (R 4.1.0)
   farver                 2.1.0    2021-02-28 [1] CRAN (R 4.1.0)
   fastmap                1.1.0    2021-01-25 [1] CRAN (R 4.1.0)
   fastmatch              1.1-3    2021-07-23 [1] CRAN (R 4.1.0)
   fgsea                  1.20.0   2021-10-26 [1] Bioconductor
   generics               0.1.1    2021-10-25 [1] CRAN (R 4.1.0)
   GenomeInfoDb         * 1.30.0   2021-10-26 [1] Bioconductor
   GenomeInfoDbData       1.2.7    2021-11-16 [1] Bioconductor
   GenomicRanges        * 1.46.1   2021-11-18 [1] Bioconductor
   ggbeeswarm             0.6.0    2017-08-07 [1] CRAN (R 4.1.0)
   ggforce                0.3.3    2021-03-05 [1] CRAN (R 4.1.0)
   ggfun                  0.0.4    2021-09-17 [1] CRAN (R 4.1.0)
   ggplot2              * 3.3.5    2021-06-25 [1] CRAN (R 4.1.0)
   ggplotify              0.1.0    2021-09-02 [1] CRAN (R 4.1.0)
   ggraph                 2.0.5    2021-02-23 [1] CRAN (R 4.1.0)
   ggrepel              * 0.9.1    2021-01-15 [1] CRAN (R 4.1.0)
   ggside                 0.1.3    2021-10-24 [1] CRAN (R 4.1.0)
   ggspavis               1.0.0    2021-10-26 [1] Bioconductor
   ggtree                 3.2.1    2021-11-16 [1] Bioconductor
   ggupset              * 0.3.0    2020-05-05 [1] CRAN (R 4.1.0)
 V glue                   1.5.0    2021-11-30 [1] CRAN (R 4.1.1) (on disk 1.5.1)
   GO.db                  3.14.0   2021-11-30 [1] Bioconductor
   GOSemSim               2.20.0   2021-10-26 [1] Bioconductor
   graphlayouts           0.7.2    2021-11-21 [1] CRAN (R 4.1.0)
   gridExtra            * 2.3      2017-09-09 [1] CRAN (R 4.1.0)
   gridGraphics           0.5-1    2020-12-13 [1] CRAN (R 4.1.0)
   grr                    0.9.5    2016-08-26 [1] CRAN (R 4.1.0)
   gtable                 0.3.0    2019-03-25 [1] CRAN (R 4.1.0)
   HDF5Array              1.22.1   2021-11-14 [1] Bioconductor
   here                   1.0.1    2020-12-13 [1] CRAN (R 4.1.0)
   hms                    1.1.1    2021-09-26 [1] CRAN (R 4.1.0)
   htmltools              0.5.2    2021-08-25 [1] CRAN (R 4.1.0)
   httr                   1.4.2    2020-07-20 [1] CRAN (R 4.1.0)
   igraph                 1.2.9    2021-11-23 [1] CRAN (R 4.1.0)
   IRanges              * 2.28.0   2021-10-26 [1] Bioconductor
   irlba                  2.3.3    2019-02-05 [1] CRAN (R 4.1.0)
   jquerylib              0.1.4    2021-04-26 [1] CRAN (R 4.1.0)
   jsonlite               1.7.2    2020-12-09 [1] CRAN (R 4.1.0)
   KEGGREST               1.34.0   2021-10-26 [1] Bioconductor
   knitr                  1.36     2021-09-29 [1] CRAN (R 4.1.0)
   labeling               0.4.2    2020-10-20 [1] CRAN (R 4.1.0)
   lattice                0.20-45  2021-09-22 [1] CRAN (R 4.1.0)
   lazyeval               0.2.2    2019-03-15 [1] CRAN (R 4.1.0)
   lifecycle              1.0.1    2021-09-24 [1] CRAN (R 4.1.0)
   limma                  3.50.0   2021-10-26 [1] Bioconductor
   locfit                 1.5-9.4  2020-03-25 [1] CRAN (R 4.1.0)
   magick                 2.7.3    2021-08-18 [1] CRAN (R 4.1.0)
   magrittr             * 2.0.1    2020-11-17 [1] CRAN (R 4.1.0)
   MASS                   7.3-54   2021-05-03 [1] CRAN (R 4.1.1)
   Matrix                 1.3-4    2021-06-01 [1] CRAN (R 4.1.1)
   Matrix.utils           0.9.8    2020-02-26 [1] CRAN (R 4.1.0)
   MatrixGenerics       * 1.6.0    2021-10-26 [1] Bioconductor
   matrixStats          * 0.61.0   2021-09-17 [1] CRAN (R 4.1.0)
   memoise                2.0.1    2021-11-26 [1] CRAN (R 4.1.0)
   munsell                0.5.0    2018-06-12 [1] CRAN (R 4.1.0)
   nlme                   3.1-153  2021-09-07 [1] CRAN (R 4.1.0)
   org.Hs.eg.db         * 3.14.0   2021-11-16 [1] Bioconductor
   patchwork              1.1.1    2020-12-17 [1] CRAN (R 4.1.0)
   pillar                 1.6.4    2021-10-18 [1] CRAN (R 4.1.1)
   pkgconfig              2.0.3    2019-09-22 [1] CRAN (R 4.1.0)
   plyr                   1.8.6    2020-03-03 [1] CRAN (R 4.1.0)
   png                    0.1-7    2013-12-03 [1] CRAN (R 4.1.0)
   polyclip               1.10-0   2019-03-14 [1] CRAN (R 4.1.0)
   purrr                  0.3.4    2020-04-17 [1] CRAN (R 4.1.0)
   qvalue                 2.26.0   2021-10-26 [1] Bioconductor
   R.methodsS3            1.8.1    2020-08-26 [1] CRAN (R 4.1.0)
   R.oo                   1.24.0   2020-08-26 [1] CRAN (R 4.1.0)
   R.utils                2.11.0   2021-09-26 [1] CRAN (R 4.1.0)
   R6                     2.5.1    2021-08-19 [1] CRAN (R 4.1.0)
   RColorBrewer           1.1-2    2014-12-07 [1] CRAN (R 4.1.0)
   Rcpp                   1.0.7    2021-07-07 [1] CRAN (R 4.1.0)
   RCurl                  1.98-1.5 2021-09-17 [1] CRAN (R 4.1.0)
 V readr                  2.1.0    2021-11-30 [1] CRAN (R 4.1.1) (on disk 2.1.1)
   reshape2               1.4.4    2020-04-09 [1] CRAN (R 4.1.0)
   rhdf5                  2.38.0   2021-10-26 [1] Bioconductor
   rhdf5filters           1.6.0    2021-10-26 [1] Bioconductor
   Rhdf5lib               1.16.0   2021-10-26 [1] Bioconductor
   rjson                  0.2.20   2018-06-08 [1] CRAN (R 4.1.0)
   rlang                  0.4.12   2021-10-18 [1] CRAN (R 4.1.1)
   rmarkdown              2.11     2021-09-14 [1] CRAN (R 4.1.0)
   rprojroot              2.0.2    2020-11-15 [1] CRAN (R 4.1.0)
   RSQLite                2.2.8    2021-08-21 [1] CRAN (R 4.1.0)
   rstudioapi             0.13     2020-11-12 [1] CRAN (R 4.1.0)
   rsvd                   1.0.5    2021-04-16 [1] CRAN (R 4.1.0)
   S4Vectors            * 0.32.3   2021-11-21 [1] Bioconductor
   sass                   0.4.0    2021-05-12 [1] CRAN (R 4.1.0)
   ScaledMatrix           1.2.0    2021-10-26 [1] Bioconductor
   scales                 1.1.1    2020-05-11 [1] CRAN (R 4.1.0)
   scater                 1.22.0   2021-10-26 [1] Bioconductor
   scatterpie             0.1.7    2021-08-20 [1] CRAN (R 4.1.0)
   scpcaTools             0.1.2    2021-10-12 [1] Github (AlexsLemonade/scpcaTools@2cdad4c)
   scuttle                1.4.0    2021-10-26 [1] Bioconductor
   sessioninfo            1.2.1    2021-11-02 [1] CRAN (R 4.1.0)
   shadowtext             0.0.9    2021-09-19 [1] CRAN (R 4.1.0)
   SingleCellExperiment * 1.16.0   2021-10-26 [1] Bioconductor
   sparseMatrixStats      1.6.0    2021-10-26 [1] Bioconductor
   SpatialExperiment    * 1.4.0    2021-10-26 [1] Bioconductor
   stringi                1.7.6    2021-11-29 [1] CRAN (R 4.1.1)
   stringr                1.4.0    2019-02-10 [1] CRAN (R 4.1.0)
   SummarizedExperiment * 1.24.0   2021-10-26 [1] Bioconductor
   tibble                 3.1.6    2021-11-07 [1] CRAN (R 4.1.0)
   tidygraph              1.2.0    2020-05-12 [1] CRAN (R 4.1.0)
   tidyr                  1.1.4    2021-09-27 [1] CRAN (R 4.1.0)
   tidyselect             1.1.1    2021-04-30 [1] CRAN (R 4.1.0)
   tidytree               0.3.6    2021-11-12 [1] CRAN (R 4.1.0)
   tinytex                0.35     2021-11-04 [1] CRAN (R 4.1.0)
   treeio                 1.18.1   2021-11-14 [1] Bioconductor
   tweenr                 1.0.2    2021-03-23 [1] CRAN (R 4.1.0)
   tzdb                   0.2.0    2021-10-27 [1] CRAN (R 4.1.1)
   utf8                   1.2.2    2021-07-24 [1] CRAN (R 4.1.0)
   vctrs                  0.3.8    2021-04-29 [1] CRAN (R 4.1.0)
   vipor                  0.4.5    2017-03-22 [1] CRAN (R 4.1.0)
   viridis                0.6.2    2021-10-13 [1] CRAN (R 4.1.0)
   viridisLite            0.4.0    2021-04-13 [1] CRAN (R 4.1.0)
 V vroom                  1.5.6    2021-11-30 [1] CRAN (R 4.1.1) (on disk 1.5.7)
 V withr                  2.4.2    2021-11-30 [1] CRAN (R 4.1.1) (on disk 2.4.3)
   xfun                   0.28     2021-11-04 [1] CRAN (R 4.1.0)
   XVector                0.34.0   2021-10-26 [1] Bioconductor
   yaml                   2.2.1    2020-02-01 [1] CRAN (R 4.1.0)
   yulab.utils            0.0.4    2021-10-09 [1] CRAN (R 4.1.0)
   zlibbioc               1.40.0   2021-10-26 [1] Bioconductor

 [1] /Library/Frameworks/R.framework/Versions/4.1/Resources/library

 V ── Loaded and on-disk version mismatch.

─────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiU3BhdGlhbCBUcmFuc2NyaXB0b21pY3MgQmVuY2htYXJraW5nIgphdXRob3I6ICJBbGx5IEhhd2tpbnMgZm9yIENDREwiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKSW4gdGhpcyBub3RlYm9vayB3ZSBhcmUgY29tcGFyaW5nIHRoZSB1c2Ugb2YgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgZm9yIHF1YW50aWZ5aW5nIHNwYXRpYWwgdHJhbnNjcmlwdG9taWNzIGxpYnJhcmllcy4gClR3byBzcGF0aWFsIHRyYW5zY3JpcHRvbWljIGxpYnJhcmllcyB3ZXJlIHF1YW50aWZpZWQgdXNpbmcgQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgYW5kIHRoZSByZXN1bHRzIHdlcmUgY29tYmluZWQgZm9sbG93aW5nIHRoZSBbQWxldmluLWZyeSB0dXRvcmlhbF0oaHR0cHM6Ly9jb21iaW5lLWxhYi5naXRodWIuaW8vYWxldmluLWZyeS10dXRvcmlhbHMvMjAyMS9hZi1zcGF0aWFsLykuIApXZSBhcmUgY29tcGFyaW5nIHRoYXQgdG8gaWYgd2Ugd2VyZSB0byBub3QgaW50ZWdyYXRlIHRoZSBTcGFjZXJhbmdlciBkYXRhIHdpdGggQWxldmluLWZyeSBhbmQgb25seSB1c2UgU3BhY2VyYW5nZXIuIApGb3Igc2ltcGxpY2l0eSB3ZSB3aWxsIG9ubHkgbG9vayBhdCBvbmUgbGlicmFyeSwgU0NQQ1IwMDAzNzIuCgojIyBTZXQgVXAKCmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCmxpYnJhcnkoU3BhdGlhbEV4cGVyaW1lbnQpCmxpYnJhcnkoZ2d1cHNldCkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkob3JnLkhzLmVnLmRiKQpgYGAKCmBgYHtyfQojIGxvYWQgaW4gYmVuY2htYXJraW5nIGZ1bmN0aW9ucyB0aGF0IHdpbGwgYmUgdXNlZCBmb3IgY29weWluZyBkYXRhIGFuZCBnZW5lcmF0aW5nIHNhbXBsZSB0YWJsZXMKZnVuY3Rpb25fcGF0aCA8LSBmaWxlLnBhdGgoIi4uIiAsImJlbmNobWFya2luZy1mdW5jdGlvbnMiLCAiUiIpCmZpbGUucGF0aChmdW5jdGlvbl9wYXRoLCBsaXN0LmZpbGVzKGZ1bmN0aW9uX3BhdGgsIHBhdHRlcm4gPSAiKi5SJCIpKSAlPiUKICBwdXJycjo6d2Fsayhzb3VyY2UpCmBgYAoKCmBgYHtyfQojIHNldCB1cCBmaWxlIHBhdGhzIApiYXNlX2RpciA8LSBoZXJlOjpoZXJlKCkKCiMgZm9sZGVyIHdpdGggYWxldmluLWZyeSBhbmQgY2VsbHJhbmdlciBxdWFudHMgZnJvbSBTMwpkYXRhX2RpciA8LSBmaWxlLnBhdGgoYmFzZV9kaXIsICJkYXRhIiwgInNwYXRpYWwiKSAKcXVhbnRzX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJkYXRhIiwgInF1YW50cyIpCgojIHJlc3VsdHMgZGlyZWN0b3J5IApyZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJyZXN1bHRzIikKCiMgc2FtcGxlIG5hbWUKc2FtcGxlX2lkIDwtIGMoIlNDUENSMDAwMzcyIikKYGBgCgoKYGBge3J9Cm1pdG9fZmlsZSA8LSBmaWxlLnBhdGgoYmFzZV9kaXIsICJzYW1wbGUtaW5mbyIsICJIb21vX3NhcGllbnMuR1JDaDM4LjEwMy5taXRvZ2VuZXMudHh0IikKICAKIyByZWFkIGluIG1pdG8gZ2VuZXMgCm1pdG9fZ2VuZXMgPC0gcmVhZHI6OnJlYWRfdHN2KG1pdG9fZmlsZSwgY29sX25hbWVzID0gImdlbmVfaWQiKQptaXRvX2dlbmVzIDwtIG1pdG9fZ2VuZXMgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9pZCkgJT4lCiAgdW5pcXVlKCkKYGBgCgoKIyMgQWxldmluLWZyeSArIFNwYWNlcmFuZ2VyIHZlcnN1cyBTcGFjZXJhbmdlciBPbmx5CgpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgY29tcGFyaW5nIHRoZSB0d28gbWV0aG9kcyBvZiB1c2luZyBBbGV2aW4tZnJ5ICsgU3BhY2VyYW5nZXIgdG8gb25seSBTcGFjZXJhbmdlciBmb3IgcXVhbnRpZmljYXRpb24uIApUbyBkbyB0aGlzLCB3ZSB3aWxsIHJlYWQgaW4gdGhlIEFsZXZpbi1mcnkgKyBTcGFjZXJhbmdlciBjb21iaW5lZCBhbmQgU3BhY2VyYW5nZXIgb25seSBgU3BhdGlhbEV4cGVyaW1lbnRgIG9iamVjdHMgc2VwYXJhdGVseSBhbmQgdGhlbiBtZXJnZSB0aGVtIGludG8gb25lIGxpc3QgYmVmb3JlIGdyYWJiaW5nIHRoZSBwZXIgY2VsbCBhbmQgcGVyIGdlbmUgcXVhbGl0eSBtZXRyaWNzLiAKCiMjIyBDcmVhdGUgU3BhdGlhbCBFeHBlcmltZW50cyAKCmBgYHtyfQojIGdldCBwYXRoIHRvIGZyeSBvdXRwdXQgZGlyZWN0b3J5IApmcnlfZGlyIDwtIGZpbGUucGF0aChxdWFudHNfZGlyLCAiYWxldmluLWZyeS1rbmVlIiwgc2FtcGxlX2lkKQpmcnlfZGlyIDwtIHBhc3RlMChmcnlfZGlyLCAiLXNwbGljZWRfaW50cm9uX3R4b21lX2szMS1zYWxpZ24tY3ItbGlrZS1lbS1rbmVlIikKCiMgcGF0aHMgdG8gc3BhdGlhbCBmb2xkZXJzIApjZWxscmFuZ2VyX2ZvbGRlciA8LSBwYXN0ZTAoc2FtcGxlX2lkLCAiLWNkbmEtc3BhdGlhbCIpCnNwYWNlcmFuZ2VyX2RpciA8LSBmaWxlLnBhdGgocXVhbnRzX2RpciwgImNlbGxyYW5nZXIiLCBjZWxscmFuZ2VyX2ZvbGRlcikKYGBgCgoKYGBge3J9CiMgcmVhZCBpbiBjb21iaW5lZCBmcnkgYW5kIHNwYWNlcmFuZ2VyIHNwZSAKZnJ5X3NwZSA8LSBjcmVhdGVfZnJ5X3NwYWNlcmFuZ2VyX3NwZShmcnlfZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGFjZXJhbmdlcl9kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZCkKYGBgCgpgYGB7cn0KIyBzcGFjZXJhbmdlciBvdXRwdXQgcGF0aHMKc3BhY2VyYW5nZXJfZGlyIDwtIGZpbGUucGF0aChxdWFudHNfZGlyLCAiY2VsbHJhbmdlciIsIGNlbGxyYW5nZXJfZm9sZGVyKQoKIyByZWFkIGluIHNwYWNlcmFuZ2VyIG91dHB1dCBkaXJlY3RseSB1c2luZyByZWFkMTBYVmlzaXVtCnNwYWNlcmFuZ2VyX3NwZSA8LSBjcmVhdGVfc3BhY2VyYW5nZXJfc3BlKHNwYWNlcmFuZ2VyX2Rpciwgc2FtcGxlX2lkKQpgYGAKCiMjIyBQZXIgU3BvdCBRQyBNZXRyaWNzCgpOb3cgdGhhdCB3ZSBoYXZlIHJlYWQgaW4gdGhlIGRhdGEgYW5kIGNyZWF0ZWQgb3VyIHR3byBgU3BhdGlhbEV4cGVyaW1lbnRgIG9iamVjdHMsIHdlIGNhbiBnbyBhaGVhZCBhbmQgY29tYmluZSB0aGVtIGludG8gb25lIGxpc3QgYW5kIHRoZW4gY2FsY3VsYXRlIHRoZSBwZXIgc3BvdCBRQyBtZXRyaWNzIHVzaW5nIGBzY3V0dGxlOjphZGRQZXJDZWxsUUNNZXRyaWNzKCkuYAoKYGBge3J9CiMgY3JlYXRlIG9uZSBsaXN0IHdpdGggYm90aCBzcGUncyB0b2dldGhlcgphbGxfc3BlX2xpc3QgPC0gbGlzdChmcnlfc3BlLCBzcGFjZXJhbmdlcl9zcGUpCm5hbWVzKGFsbF9zcGVfbGlzdCkgPC0gYygiYWxldmluLWZyeSIsICJzcGFjZXJhbmdlciIpCgojIGNhbGN1bGF0ZSBwZXIgY2VsbCBRQyBhbmQgb3V0cHV0IHRvIGEgY29tYmluZWQgZGF0YSBmcmFtZSB3aXRoIHBsb3R0aW5nIAphbGxfc3BlX2xpc3QgPC0gYWxsX3NwZV9saXN0ICU+JQogICAgcHVycnI6Om1hcCgKICAgICAgfiBzY3V0dGxlOjphZGRQZXJDZWxsUUNNZXRyaWNzKC54LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNldHMgPSBsaXN0KG1pdG8gPSBtaXRvX2dlbmVzW21pdG9fZ2VuZXMgJWluJSByb3duYW1lcygueCldKSkpCmBgYAoKQWZ0ZXIgYWRkaW5nIGluIHRoZSBwZXIgc3BvdCBRQyBtZXRyaWNzIHRvIGJvdGggb2YgdGhlIHNwZSdzLCB3ZSB3YW50IHRvIGV4dHJhY3QgdGhlIGBjb2xEYXRhYCBmcm9tIGVhY2ggc3BlIGFuZCBjcmVhdGUgYSBkYXRhIGZyYW1lIHRoYXQgd2UgY2FuIHVzZSBmb3IgcGxvdHRpbmcuIApXZSB3aWxsIGFsc28gbmVlZCBzb21lIGluZm9ybWF0aW9uIGFib3V0IGVhY2ggc2FtcGxlIGFuZCBob3cgaXQgd2FzIHJ1biwgc28gd2Ugd2lsbCBjcmVhdGUgYSBzYW1wbGUgbWV0YWRhdGEgdGFibGUsIGBzYW1wbGVfaW5mb19kZmAgdGhhdCB3aWxsIHRoZW4gYmUgbWVyZ2VkIHdpdGggdGhlIGBjb2xEYXRhYC4gCgpgYGB7cn0KIyBjcmVhdGUgc2FtcGxlIGluZm8gZGF0YWZyYW1lIHRvIGJlIGpvaW5lZCB3aXRoIHBlciBzcG90IGRhdGFmcmFtZSBsYXRlcgpzYW1wbGVfaW5mb19kZiA8LSBxdWFudF9pbmZvX3RhYmxlKGRhdGFfZGlyPSBxdWFudHNfZGlyLCAKICAgICAgICAgICAgICAgICB0b29scyA9IGMoImNlbGxyYW5nZXIiLCAiYWxldmluLWZyeS1rbmVlIiksCiAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IHNhbXBsZV9pZCkgJT4lCiAgIyBjb252ZXJ0IGNlbGxyYW5nZXIgdG8gc3BhY2VyYW5nZXIgCiAgZHBseXI6Om11dGF0ZSh0b29sID0gaWZlbHNlKHRvb2wgPT0gImNlbGxyYW5nZXIiLCAic3BhY2VyYW5nZXIiLCB0b29sKSkKCnNhbXBsZV9pbmZvX2RmCmBgYAoKV2hlbiB3ZSBjb252ZXJ0IHRoZSBgY29sRGF0YWAgdG8gYSBkYXRhIGZyYW1lIHdlIHVzZSB0aGUgY3VzdG9tIGZ1bmN0aW9uLCBgc3BhdGlhbF9jb2xkYXRhX3RvX2RmKClgIHRvIGRvIHNvIGFuZCBhcHBseSBpdCB0byBlYWNoIHNwZSBpbiBvdXIgbGlzdC4gCmBgYHtyfQojIGpvaW4gY29sZGF0YSBkYXRhZnJhbWUgd2l0aCBzYW1wbGUgaW5mbwpjb2xkYXRhX2RmIDwtIGFsbF9zcGVfbGlzdCAlPiUKICBwdXJycjo6bWFwX2RmKHNwYXRpYWxfY29sZGF0YV90b19kZiwgLmlkID0gInRvb2wiKSAlPiUKICAjIHJlbW92ZSBleHRyYSAtMSBmcm9tIHNwYWNlcmFuZ2VyIGJhcmNvZGVzCiAgZHBseXI6Om11dGF0ZShzcG90X2lkID0gZ3N1YigiLTEiLCAiIiwgc3BvdF9pZCkpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oc2FtcGxlX2luZm9fZGYsCiAgICAgICAgICAgICAgICAgICBieSA9IGMoInRvb2wiLCAic2FtcGxlX2lkIiA9ICJzYW1wbGUiKSkKYGBgCgpOb3cgd2Ugb25seSB3YW50IHRvIGZpbHRlciBvdXIgZGF0YSBmcmFtZSB0byBjb250YWluIHNwb3RzIHRoYXQgYXJlIHNoYXJlZCBiZXR3ZWVuIGJvdGggdG9vbHMgYW5kIHRob3NlIHRoYXQgYXJlIGZvdW5kIHRvIGJlIG92ZXJsYXBwaW5nIHdpdGggdGhlIHRpc3N1ZS4gCgpgYGB7cn0KIyBpZGVudGlmeSBzaGFyZWQgc3BvdHMgb25seSAKc3BvdF9jb3VudHMgPC0gY29sZGF0YV9kZiAlPiUgIAogIGRwbHlyOjpjb3VudChzcG90X2lkLCBzYW1wbGVfaWQpCmBgYAoKYGBge3J9CiMgaG93IG1hbnkgc3BvdHMgYXJlIHNoYXJlZCBhbW9uZyB0aGUgdG9vbHMKc3BvdF9jb3VudHNfcGxvdCA8LSBjb2xkYXRhX2RmICU+JQogIGRwbHlyOjpncm91cF9ieShzcG90X2lkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHRvb2xzX2RldGVjdGVkID0gbGlzdCh1bmlxdWUodG9vbCkpKQoKZ2dwbG90KHNwb3RfY291bnRzX3Bsb3QsIGFlcyh4ID0gdG9vbHNfZGV0ZWN0ZWQpKSsKICBnZW9tX2JhcigpICsKICBzY2FsZV94X3Vwc2V0KG5faW50ZXJzZWN0aW9ucyA9IDMpCmBgYApGb3IgdGhlIG1vc3QgcGFydCwgdGhlIG1ham9yaXR5IG9mIHRoZSBzcG90cyBpZGVudGlmaWVkIGFyZSBmb3VuZCBpbiBib3RoIFNwYWNlcmFuZ2VyIGFsb25lIGFuZCB0aGUgY29tYmluYXRpb24gd2l0aCBBbGV2aW4tZnJ5LCB3aXRoIGEgc21hbGwgc3Vic2V0IGJlaW5nIGlkZW50aWZpZWQgaW4gU3BhY2VyYW5nZXIgYWxvbmUuIAoKTGV0J3MgZmlsdGVyIHRvIG9ubHkgaW5jbHVkZSB0aGVzZSBjb21tb24gc3BvdHMgYW5kIHRob3NlIHRoYXQgYXJlIGZvdW5kIHRvIGJlIG92ZXJsYXBwaW5nIHRoZSB0aXNzdWUuCgpgYGB7cn0KY29tbW9uX3Nwb3RzIDwtIHNwb3RfY291bnRzICU+JQogIGRwbHlyOjpmaWx0ZXIobiA9PSAyKSAlPiUKICBkcGx5cjo6cHVsbChzcG90X2lkKQoKY29sZGF0YV9kZl9jb21tb24gPC0gY29sZGF0YV9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKHNwb3RfaWQgJWluJSBjb21tb25fc3BvdHMsCiAgICAgICAgICAgICAgICAjIG9ubHkgaW5jbHVkZSBzcG90cyB0aGF0IG92ZXJsYXAgd2l0aCB0aXNzdWUKICAgICAgICAgICAgICAgIGluX3Rpc3N1ZSA9PSAxKQpgYGAKCldlIHdpbGwgYWxzbyBuZWVkIHRvIGZpbHRlciB0aGUgc3BlJ3MgZGlyZWN0bHkgYmFzZWQgb24gc3BvdHMgdGhhdCBhcmUgcHJlc2VudCBpbiB0aGUgdGlzc3VlLCBzbyB3ZSBjcmVhdGUgYSBzbWFsbCBmdW5jdGlvbiB0byBkbyB0aGlzIGFuZCB0aGVuIGFwcGx5IGl0IHRvIGJvdGggc3BlJ3MgaW4gdGhlIGxpc3QuIAoKYGBge3J9CiMgd2Ugd2lsbCBhbHNvIHdhbnQgdG8gZmlsdGVyIHRoZSBzcGUncyBkaXJlY3RseSAKZmlsdGVyX3NwZSA8LSBmdW5jdGlvbihzcGUpewogIHNwZSA8LSBzcGVbLCBzcGF0aWFsRGF0YShzcGUpJGluX3Rpc3N1ZSA9PSAxXQp9CgphbGxfc3BlX2ZpbHRlciA8LSBhbGxfc3BlX2xpc3QgJT4lCiAgcHVycnI6Om1hcChmaWx0ZXJfc3BlKQpgYGAKCgpXaGVuIHdlIGxvb2sgYXQgb3VyIHJlc3VsdHMsIHdlIHdpbGwgYWxzbyB3YW50IHRvIHZpc3VhbGl6ZSB0aGVtIHNvIHdlIHdpbGwgbWFrZSBhIGN1c3RvbSBmdW5jdGlvbiB0byBwbG90IHRoZSByZXN1bHRzLgoKYGBge3J9CiMgY3VzdG9tIGZ1bmN0aW9uIGZvciBwbG90dGluZyBzcGUgcmVzdWx0cyBhbmQgY29sb3JpbmcgYnkgY29sdW1uIG9mIGNvbERhdGEgb2YgY2hvaWNlCnBsb3Rfc3BlIDwtIGZ1bmN0aW9uKHNwZSwgc2FtcGxlLCBjb2x1bW4pewogICMgcGxvdCBzcG90cyBvbmx5IAogIHAxIDwtIGdnc3BhdmlzOjpwbG90U3BvdHMoc3BlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfY29vcmQgPSAicHhsX2NvbF9pbl9mdWxscmVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5X2Nvb3JkID0gInB4bF9yb3dfaW5fZnVsbHJlcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGUgPSBjb2x1bW4pICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCiAgCiAgIyBwbG90IHdpdGggdGlzc3VlIHVuZGVybmVhdGgKICBwMiA8LSBnZ3NwYXZpczo6cGxvdFZpc2l1bShzcGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhfY29vcmQgPSAicHhsX2NvbF9pbl9mdWxscmVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeV9jb29yZCA9ICJweGxfcm93X2luX2Z1bGxyZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gY29sdW1uKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpCiAgCiAgIyBhcnJhbmdlIHBsb3RzIGFuZCBhZGQgc2FtcGxlIG5hbWUgYXMgdGl0bGUgCiAgZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbnJvdyA9IDEsIHRvcCA9IGdyaWQ6OnRleHRHcm9iKHNhbXBsZSkpCn0KYGBgCgpGaXJzdCB3ZSB3aWxsIGxvb2sgYXQgdGhlIHBlciBjZWxsIG1ldHJpY3M6IG1pdG9jaG9uZHJpYWwgcmVhZHMgcGVyIGNlbGwsIHRvdGFsIFVNSSBwZXIgY2VsbCwgYW5kIHRvdGFsIGdlbmVzIGRldGVjdGVkIHBlciBjZWxsLiAKCmBgYHtyfQojICUgbWl0b2Nob25kcmlhbCByZWFkcy8gc3BvdCAKZ2dwbG90KGNvbGRhdGFfZGZfY29tbW9uLCBhZXMoeCA9IHRvb2wsIHkgPSBzdWJzZXRzX21pdG9fcGVyY2VudCwgZmlsbCA9IHRvb2wpKSArIAogIGdlb21fYm94cGxvdCgpICsKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIk1pdG8gUGVyY2VudCIpICsgCiAgeGxhYigiIikKYGBgCgpgYGB7cn0KYWxsX3NwZV9maWx0ZXIgJT4lCiAgcHVycnI6Oml3YWxrKHBsb3Rfc3BlLCBjb2x1bW4gPSAic3Vic2V0c19taXRvX3BlcmNlbnQiKQpgYGAKT3ZlcmFsbCBpdCBsb29rcyBsaWtlIG1pdG9jaG9uZHJpYWwgY29udGVudCBpcyBsb3cgYW5kIGZhaXJseSBzaW1pbGFyIGFjcm9zcyBib3RoIHRvb2xzLiAKCmBgYHtyfQojIHRvdGFsIFVNSS8gc3BvdCAKZ2dwbG90KGNvbGRhdGFfZGZfY29tbW9uLCBhZXMoeCA9IHN1bSwgY29sb3IgPSB0b29sKSkgKyAKICBnZW9tX2RlbnNpdHkoKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsgCiAgeWxhYigiVU1JL3Nwb3QiKSArIAogIHhsYWIoIiIpCmBgYAoKYGBge3J9CmFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjppd2FsayhwbG90X3NwZSwgY29sdW1uID0gInN1bSIpCmBgYAoKCmBgYHtyfQojIHRvdGFsIGdlbmVzLyBzcG90IApnZ3Bsb3QoY29sZGF0YV9kZl9jb21tb24sIGFlcyh4ID0gZGV0ZWN0ZWQsIGNvbG9yID0gdG9vbCkpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKyAKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArIAogIHlsYWIoIkdlbmVzIGRldGVjdGVkL3Nwb3QiKSArIAogIHhsYWIoIiIpIApgYGAKCmBgYHtyfQphbGxfc3BlX2ZpbHRlciAlPiUKICBwdXJycjo6aXdhbGsocGxvdF9zcGUsIGNvbHVtbiA9ICJkZXRlY3RlZCIpCmBgYAoKR2VuZXJhbGx5IGl0IGxvb2tzIGxpa2UgdGhlcmUgYXJlIHNvbWUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdG9vbHMuIApXaGVuIGxvb2tpbmcgYXQgdGhpcyBjbG9zZXIsIGl0IGFwcGVhcnMgdGhhdCBBbGV2aW4tZnJ5IHByb3ZpZGVzIGFuIGluY3JlYXNlIGluIGJvdGggdG90YWwgVU1JL2NlbGwgYW5kIGdlbmVzIGRldGVjdGVkL2NlbGwgd2hpY2ggaXMgc2VlbiBpbiB0aGUgc3BhdGlhbCBwbG90IGFzIHdlbGwuIAoKCiMjIyBQZXIgR2VuZSBRQyBNZXRyaWNzCgpMZXQncyBhbHNvIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIG9mIG1lYW4gZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBzaGFyZWQgZ2VuZXMuIApXZSB3aWxsIGZpcnN0IG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBwZXIgZmVhdHVyZSBRQyBvbiB0aGUgZmlsdGVyZWQgc3BlcyBhZnRlciByZW1vdmluZyBzcG90cyBub3QgcHJlc2VudCBpbiB0aGUgdGlzc3VlIGFuZCB0aGVuIGdyYWIgdGhlIGByb3dEYXRhYCBhbmQgY29tYmluZSBpbnRvIGEgZGF0YSBmcmFtZSB1c2VkIGZvciBwbG90dGluZy4gCgpgYGB7cn0KYWxsX3NwZV9maWx0ZXIgPC0gYWxsX3NwZV9maWx0ZXIgJT4lCiAgcHVycnI6Om1hcChzY3V0dGxlOjphZGRQZXJGZWF0dXJlUUNNZXRyaWNzKQpgYGAKCmBgYHtyfQojIGdyYWIgcm93ZGF0YSBhbmQgY29tYmluZSB3aXRoIHNhbXBsZSBpbmZvCnJvd2RhdGFfZGYgPC0gcHVycnI6Om1hcF9kZihhbGxfc3BlX2ZpbHRlciwgc2NwY2FUb29sczo6cm93ZGF0YV90b19kZiwgLmlkID0gInRvb2wiKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHNhbXBsZV9pbmZvX2RmLAogICAgICAgICAgICAgICAgICAgYnkgPSBjKCJ0b29sIikpCmBgYAoKV2UgdGhlbiB3YW50IHRvIGZpbHRlciBvdXQgYW55IGxvd2x5IGRldGVjdGVkIGdlbmVzLCAoZGV0ZWN0ZWQgPCA1LjApIGFuZCByZXN0cmljdCBvdXIgYW5hbHlzaXMgdG8gdGhvc2UgZ2VuZXMgdGhhdCBhcmUgZm91bmQgaW4gYm90aCB0b29scy4gCgpgYGB7cn0KZ2VuZV9jb3VudHMgPC0gcm93ZGF0YV9kZiAlPiUgCiAgIyByZW1vdmUgZ2VuZXMgdGhhdCBoYXZlIGEgbG93IGZyZXF1ZW5jeSBvZiBiZWluZyBkZXRlY3RlZAogIGRwbHlyOjpmaWx0ZXIoZGV0ZWN0ZWQgPj0gNS4wKSAlPiUKICBkcGx5cjo6Y291bnQoZ2VuZV9pZCwgc2FtcGxlKQoKIyByZXN0cmljdCB0byBvbmx5IGNvbW1vbiBnZW5lcyAKY29tbW9uX2dlbmVzIDwtIGdlbmVfY291bnRzICU+JQogIGRwbHlyOjpmaWx0ZXIobiA9PSAyKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKQoKcm93ZGF0YV9kZl9jb21tb24gPC0gcm93ZGF0YV9kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGdlbmVfaWQgJWluJSBjb21tb25fZ2VuZXMpIApgYGAKCmBgYHtyfQojIGNyZWF0ZSBhIHRhYmxlIHRvIGNhbGN1bGF0ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIG1lYW4gZ2VuZSBleHByZXNzaW9uCnJvd2RhdGFfY29yIDwtIHJvd2RhdGFfZGZfY29tbW9uICU+JQogIGRwbHlyOjpzZWxlY3QodG9vbCwgZ2VuZV9pZCwgc2FtcGxlLCBtZWFuKSAlPiUKICAjIHNwcmVhZCB0aGUgbWVhbiBleHByZXNzaW9uIHN0YXRzIHRvIG9uZSBjb2x1bW4gcGVyIGNhbGxlcgogIHRpZHlyOjpwaXZvdF93aWRlcihpZF9jb2xzID0gYyhnZW5lX2lkLCBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBuYW1lc19mcm9tID0gYygidG9vbCIpLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IG1lYW4pICU+JQogICMgZHJvcCByb3dzIHdpdGggTkEgdmFsdWVzIHRvIGVhc2UgY29ycmVsYXRpb24gY2FsY3VsYXRpb24KICB0aWR5cjo6ZHJvcF9uYSgpCmBgYAoKYGBge3J9CiMgbG9vayBhdCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdG9vbHMKcm93ZGF0YV9jb3IgJT4lIAogIGRwbHlyOjpncm91cF9ieShzYW1wbGUpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBhbGV2aW5fZnJ5X2tuZWVfc3BhY2VyYW5nZXJfY29yID0gY29yKGBzcGFjZXJhbmdlcmAsIGBhbGV2aW4tZnJ5YCwgbWV0aG9kID0gInNwZWFybWFuIikKICApCmBgYAoKYGBge3J9CiMgbWVhbiBnZW5lIGV4cHJlc3Npb24gYWNyb3NzIHNoYXJlZCBnZW5lcyAKZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBnZW9tX2FibGluZSgpICsKICBsYWJzKHggPSAiU3BhY2VyYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnkgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKQogIApgYGAKQ29ycmVsYXRpb24gYXBwZWFycyB0byBiZSBxdWl0ZSBoaWdoIGJldHdlZW4gbWVhbiBnZW5lIGV4cHJlc3Npb24gaW4gU3BhY2VyYW5nZXIgYW5kIEFsZXZpbi1mcnksIGhvd2V2ZXIsIHdlIGRvIHNlZSB0aGF0IHRoZXJlIGlzIGEgc3Vic2V0IG9mIGdlbmVzIHRoYXQgaGF2ZSBoaWdoZXIgZ2VuZSBleHByZXNzaW9uIGluIFNwYWNlcmFuZ2VyIHRoYW4gaW4gQWxldmluLWZyeSBhbmQgYXJlIHNsaWdodGx5IG9mZiB0aGUgZGlhZ29uYWwuIAoKCgpgYGB7cn0KIyBnZXQgdGhlIGdlbmUgc3ltYm9scyBhbmQgdGhlbiBqb2luIGJhY2sgd2l0aCByb3dkYXRhIGRmIApnZW5lX3N5bWJvbHNfZGYgPC0gcm93ZGF0YV9kZiAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVfaWQsIHN5bWJvbCkKCiMgam9pbiBjb3JyZWxhdGlvbiB3aXRoIGdlbmUgc3ltYm9scyAKcm93ZGF0YV9jb3IgPC0gcm93ZGF0YV9jb3IgJT4lCiAgZHBseXI6OmxlZnRfam9pbihnZW5lX3N5bWJvbHNfZGYpICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpICU+JQogIHRpZHlyOjpkcm9wX25hKCkgJT4lCiAgIyBhZGQgZGlmZmVyZW5jZSBpbiBtZWFuIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIGFsZXZpbi1mcnkgYW5kIHNwYWNlcmFuZ2VyIAogIGRwbHlyOjptdXRhdGUobG9nX2ZvbGRfY2hhbmdlID0gbG9nKGBhbGV2aW4tZnJ5YC9zcGFjZXJhbmdlcikpCgojIHByaW50IG91dCBsaXN0IG9mIGdlbmVzIHdpdGggd2l0aCA+IDEuNSBnZW5lIGV4cHJlc3Npb24gaW4gQWxldmluLWZyeQpyb3dkYXRhX2NvciAlPiUKICBkcGx5cjo6ZmlsdGVyKGFicyhsb2dfZm9sZF9jaGFuZ2UpID4gMC41KSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvZ19mb2xkX2NoYW5nZSkpICU+JQogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlLCBzeW1ib2wsIGdlbmVfaWQsIGxvZ19mb2xkX2NoYW5nZSwgYGFsZXZpbi1mcnlgLCBzcGFjZXJhbmdlcikKYGBgCgpgYGB7cn0KZ2dwbG90KHJvd2RhdGFfY29yLCBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMC41LCBhbHBoYSA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyAKICBzY2FsZV95X2xvZzEwKCkgKyAKICBnZW9tX2FibGluZSgpICsKICBsYWJzKHggPSAiU3BhY2VyYW5nZXIgbWVhbiBnZW5lIGV4cHJlc3Npb24iLCB5ID0gIkFsZXZpbiBGcnkgTWVhbiBnZW5lIGV4cHJlc3Npb24iKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gcm93ZGF0YV9jb3IgJT4lCiAgICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihhYnMobG9nX2ZvbGRfY2hhbmdlKSA+IDAuNSksCiAgICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGBzcGFjZXJhbmdlcmAsIHkgPSBgYWxldmluLWZyeWAsIGxhYmVsID0gc3ltYm9sKSwKICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzPSA1MCwgc2l6ZT0gMikKYGBgCkxldCdzIGxvb2sgYXQgd2hhdCB0eXBlcyBvZiBnZW5lcyBhcmUgZm91bmQgdG8gaGF2ZSBkaWZmZXJlbnQgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyB0aGVzZSB0d28gdG9vbHMgdXNpbmcgb3ZlciByZXByZXNlbnRhdGlvbiBhbmFseXNpcy4gCgpgYGB7cn0KIyB0YXJnZXQgZ2VuZSBsaXN0IApkaWZmZXJlbnRfZ2VuZXMgPC0gcm93ZGF0YV9jb3IgJT4lCiAgIyBmaWx0ZXIgZm9yIGFueXRoaW5nIHdpdGggZm9sZCBjaGFuZ2UgPiAxLjUKICBkcGx5cjo6ZmlsdGVyKGFicyhsb2dfZm9sZF9jaGFuZ2UpID4gMC40KSAlPiUKICBkcGx5cjo6cHVsbChzeW1ib2wpICU+JQogIHVuaXF1ZSgpCgojIGJhY2tncm91bmQgZ2VuZSBsaXN0IApiYWNrZ3JvdW5kX2dlbmVzIDwtIHJvd2RhdGFfY29yJHN5bWJvbCAlPiUKICB1bmlxdWUoKQpgYGAKCgpgYGB7cn0KIyBwZXJmb3JtIGdlbmUgb250b2xvZ3kgbG9va2luZyBhdCBhbGwgZ2VuZXMgdGhhdCBhcmUgZGlmZmVyZW50IApnb19vcmFfcmVzdWx0cyA8LSBlbnJpY2hHTyhnZW5lID0gZGlmZmVyZW50X2dlbmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmRfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjAwMDAxKQpgYGAKCgpgYGB7cn0KIyBsb29rIGF0IGdlbmUgb250b2xvZ3kgcmVzdWx0cyAKZ29fcmVzdWx0cyA8LSBnb19vcmFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpmaWx0ZXIocC5hZGp1c3QgPCAwLjAwMDAxKQpnb19yZXN1bHRzCmBgYApJdCBsb29rcyBsaWtlIHRoZXJlIGFyZW4ndCBhbnkgc3BlY2lmaWMgcGF0aHdheXMgdGhhdCBhcmUgYWZmZWN0ZWQuIAoKIyMjIFNoYXJlZCBnZW5lcyBhY3Jvc3MgdG9vbHMKClRoZSBmaW5hbCB0aGluZyB3ZSB3aWxsIGxvb2sgYXQgaXMgdGhlIG92ZXJsYXAgb2YgZ2VuZXMgZGV0ZWN0ZWQgaW4gZWFjaCB0b29sLiAKRmlyc3Qgd2UgbmVlZCB0byBmaWx0ZXIgdGhlIGBTcGF0aWFsRXhwZXJpbWVudGAgb2JqZWN0cyB0byBvbmx5IGhhdmUgc3BvdHMgdGhhdCBhcmUgZm91bmQgaW4gYm90aCB0b29scyBhbmQgdGhlbiB3ZSBmaWx0ZXIgdG8gaW5jbHVkZSBvbmx5IGdlbmVzIHRoYXQgYXJlIGZvdW5kIGluIGJvdGggaW5kaWNlcyBiZWZvcmUgbG9va2luZyBhdCB0aGUgb3ZlcmxhcC4gCgpgYGB7cn0KIyBtYWtlIGEgZnVuY3Rpb24gdG8gZmlsdGVyIHNjZXMgYnkgc3Vic2V0IG9mIHNwb3RzIGFuZCByZS1jYWxjdWxhdGUgZmVhdHVyZSBzdGF0cwpmaWx0ZXJfc3BlIDwtIGZ1bmN0aW9uKHNwZSwgc3BvdHMpewogICMgcmVtb3ZlICItMSIgYXQgZW5kIG9mIGJhcmNvZGUgZm9yIHNwYWNlcmFuZ2VyIHNwZXMKICBjb2xuYW1lcyhzcGUpIDwtIGdzdWIoIi0xIiwgIiIsIGNvbG5hbWVzKHNwZSkpCiAgY2VsbHNfdG9fa2VlcCA8LSBjb2xuYW1lcyhzcGUpICVpbiUgc3BvdHMKICByb3dEYXRhKHNwZSkgPC0gTlVMTAogIHNwZVssIGNlbGxzX3RvX2tlZXBdICU+JQogICAgc2N1dHRsZTo6YWRkUGVyRmVhdHVyZVFDTWV0cmljcygpCn0KYGBgCgpgYGB7cn0KIyBmaWx0ZXIgYWxsIHNwZXMgdG8gb25seSBoYXZlIGNvbW1vbiBzcG90cyAKc3BlX2xpc3RfY29tbW9uIDwtIGFsbF9zcGVfZmlsdGVyICU+JQogIHB1cnJyOjptYXAoZmlsdGVyX3NwZSwgc3BvdHMgPSBjb21tb25fc3BvdHMpCgojIGdyYWIgcm93ZGF0YSBmcm9tIGZpbHRlcmVkIHNjZXMgCnJvd2RhdGFfZGZfZmlsdGVyZWQgPC0gcHVycnI6Om1hcF9kZihzcGVfbGlzdF9jb21tb24sIHNjcGNhVG9vbHM6OnJvd2RhdGFfdG9fZGYsIC5pZCA9ICJ0b29sIikgJT4lCiAgZHBseXI6OmxlZnRfam9pbihzYW1wbGVfaW5mb19kZiwKICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidG9vbCIpKQpgYGAKCgpgYGB7cn0KIyBnZXQgZ2VuZXMgY29tbW9uIGluIGFsbCB0b29scwpjb21tb25fZ2VuZXMgPC0gcm93ZGF0YV9kZl9maWx0ZXJlZCAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVfaWQsIHRvb2wpICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpICU+JQogIGRwbHlyOjpncm91cF9ieShnZW5lX2lkKSAlPiUKICBkcGx5cjo6dGFsbHkoKSAlPiUKICAjIGZpbHRlciBmb3IgZ2VuZXMgZm91bmQgaW4gYm90aCBzcGFjZXJhbmdlciBhbmQgYWxldmluLWZyeQogIGRwbHlyOjpmaWx0ZXIobiA9PSAyKSAlPiUKICBkcGx5cjo6cHVsbChnZW5lX2lkKQoKIyBmaWx0ZXIgcm93ZGF0YV9kZiB0byBvbmx5IGluY2x1ZGUgZ2VuZXMgZm91bmQgaW4gYWxsIHRvb2xzIGFuZCBnZW5lcyB3aXRoIG1lYW4gPiAwIGFuZCBkZXRlY3RlZCA+IDAgaW4gYWxsIGNlbGxzCnJvd2RhdGFfZGZfZmlsdGVyZWQgPC0gcm93ZGF0YV9kZl9maWx0ZXJlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihnZW5lX2lkICVpbiUgY29tbW9uX2dlbmVzKSAlPiUKICBkcGx5cjo6ZmlsdGVyKG1lYW4gPiAwICYgZGV0ZWN0ZWQgPiAwKQpgYGAKCgpgYGB7cn0KIyBjcmVhdGUgdXBzZXQgcGxvdCAKZ2VuZV9kZXRlY3RfZGYgPC0gcm93ZGF0YV9kZl9maWx0ZXJlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZ2VuZV9pZCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh0b29sc19kZXRlY3RlZCA9IGxpc3QodW5pcXVlKHRvb2wpKSkKCmdncGxvdChnZW5lX2RldGVjdF9kZiwgYWVzKHggPSB0b29sc19kZXRlY3RlZCkpICsKICBnZW9tX2JhcigpICsKICBzY2FsZV94X3Vwc2V0KG5faW50ZXJzZWN0aW9ucyA9IDMpCmBgYAoKU2ltaWxhciB0byB3aXRoIHRoZSBzaW5nbGUtY2VsbCBkYXRhIG1vc3QgZ2VuZXMgYXJlIGRldGVjdGVkIGluIGJvdGggQWxldmluLWZyeSBhbmQgU3BhY2VyYW5nZXIgd2hpY2ggaXMgZ29vZC4gCgojIyBDb25jbHVkaW5nIHRob3VnaHRzIAoKLSBBbGV2aW4tZnJ5IGFuZCBTcGFjZXJhbmdlciBvbmx5IHJlc3VsdCBpbiBzaW1pbGFyIGRpc3RyaWJ1dGlvbnMgb2YgVU1JL3Nwb3QgYW5kIGdlbmVzL3Nwb3QsIGFsdGhvdWdoIGl0IGlzIG5vdCBxdWl0ZSBhcyBuaWNlIGFzIHRoZSBvdmVybGF5IHlvdSBzZWUgd2l0aCBzaW5nbGUtY2VsbCBsaWJyYXJpZXMuIAotIEl0IGFwcGVhcnMgdGhhdCBBbGV2aW4tZnJ5IGhhcyBzbGlnaHRseSBoaWdoZXIgVU1JL2NlbGwgYW5kIGdlbmVzIGRldGVjdGVkL2NlbGwuIAotIEJvdGggdG9vbHMgYWxzbyBzaG93IGhpZ2ggY29ycmVsYXRpb24gaW4gbWVhbiBnZW5lIGV4cHJlc3Npb24gYW5kIGhpZ2ggb3ZlcmxhcCBpbiB0aGUgZ2VuZXMgdGhhdCBhcmUgZGV0ZWN0ZWQsIGFsdGhvdWdoIHRoZXJlIGlzIGEgc3Vic2V0IG9mIGdlbmVzIHRoYXQgYXJlIG9mZiB0aGUgZGlhZ29uYWwgYnV0IGhhdmUgbm8gcGFydGljdWxhciBnZW5lIHNldCBlbnJpY2htZW50LiAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25pbmZvOjpzZXNzaW9uX2luZm8oKQpgYGAKCg==